<?php


/**
 * Routes requests according the bb_menu table.
 *
 * For menus to work we need to route each menu item's url to an specific Controller's action. But even more important,
 * some menu items work as containers for other urls (for example a menu item that points to a category, will also have
 * to respond to urls of articles inside that category).
 *
 * The following examples show the kinds of urls that should be supported:
 * 
 *     1. "/" - The item marked as default, usually will be equivalent to "/Content/frontpage".
 *
 *     2. "/news/" - A top level menu item that points to a category. The category slug is 'Latest_News' and is
 *        specified in the params column of bb_menuitem. It is equivalent to "/Content/category/slug/Latest_News".
 *
 *     3. "/products/software/" - This is a sub-category inside another category. Checks the existence of all menu items
 *        up to "software" and executes "/Content/category/slug/Software".
 *
 *     4. "/products/software/Zend_Studio_Professional_Upgrade" - This one is tricky because the last part isn't one of
 *        the registed menu items. It could be an article (if it's on the ContentController), an action for another
 *        controller or something that only has a meaning inside a custom controller. What we'll do here is try
 *        to resolve all the menu items we can (in this case it will be the ContentController) and pass all "slugs" to
 *        the special routeAction() method that will decide what to do next.
 *
 *     5. "/contact/", "/users/signup/" - Those will be exactly like #2 and #3. It will redirect to the controller and
 *        action specified in the bb_menuitem table
 *
 *     6. Of course, urls in the form of "/controller/action" or "/module/controller/action" should still work.
 */
class Blackbird_Controller_Router_Route_Content extends Zend_Controller_Router_Route_Abstract
{
	static function getInstance(Zend_Config $config)
	{
		return new self();
	}

	function __construct()
	{
		$this->_db = Zend_Registry::get('database');
	}

	function match($request)
	{
		$this->_moduleKey = $request->getModuleKey();
		$this->_controllerKey = $request->getControllerKey();
		$this->_actionKey = $request->getActionKey();

		$path = trim($request->getPathInfo(), self::URI_DELIMITER);
		if ('' == $path)
			return $this->_fetchDefaultMenuItem();

		$slugs = explode(self::URI_DELIMITER, $path);
		$menuItem = $this->_fetchMenuItemByPath($slugs);
		$result = array();
		if (false !== $menuItem)
		{
			foreach ($menuItem->params as $key => $value)
			{
				$request->setParam($key, $value);
				$result[$key] = $value;
			}
			$action = $menuItem->isExactMatch
				? $menuItem->action
				: 'route';
			$request->setParam('slugs', $slugs);
			$request->setModuleName($menuItem->module);
			$request->setControllerName($menuItem->controller);
			$request->setActionName($action);
			$result[$this->_moduleKey] = $menuItem->module;
			$result[$this->_controllerKey] = $menuItem->controller;
			$result[$this->_actionKey] = $action;
			return $result;
		}
		return false;
	}

	public function assemble($data = array(), $reset = false, $encode = false)
	{
	}

	private function _fetchDefaultMenuItem()
	{
		$sql = 'select menuItemId, module, controller, action, params'
			. "\n	from bb_menuitem"
			. "\n	where isDefault <> 0"
			. "\n	order by lft, rgt"
			;
		$this->_db = Zend_Registry::get('database');
		if (($row = $this->_db->fetchRow($sql)) !== false)
		{
			parse_str($row->params, $row->params);
			return $row;
		}
		return false;
	}
	
	private function _fetchArticleIdBySlug($slug)
	{
		$sql = 'select articleId'
			. "\n	from bb_article"
			. "\n	where slug = ?"
			;
		return $this->_db->fetchOne($sql, $slug);
	}

	private function _fetchMenuItemByPath($slugs)
	{
		// TODO: Handle duplicates.
		$quoted = array();
		foreach ($slugs as $slug)
			$quoted[] = $this->_db->quote($slug);
		$sql = 'select mi.menuItemId, mi.slug, mi.module, mi.controller, mi.action, mi.params'
			. "\n	from bb_menuitem mi"
			. "\n		left join bb_menuitem p on p.menuItemId = mi.parentId"
			. "\n	where mi.slug in (" . implode(', ', $quoted) . ')'
			;
		if (count($quoted) > 1)
		{
			$sql .= ' and (p.menuItemId is null or p.slug in ('
				. implode(', ', array_slice($quoted, 0, -1)) . '))'
				;
		}
		$sql .= "\n	order by mi.lft, mi.rgt";
		$lastRow = false;
		$slugsCount = count($slugs);
		$isExactMatch = false;
		$rows = $this->_db->fetchAll($sql);
		$rowsCount = count($rows);
		for ($i = 0; $i < $slugsCount && $i < $rowsCount; ++$i)
		{
			$row = $rows[$i];
			$current = $slugs[$i];
			$isExactMatch = ($current == $row->slug);
			parse_str($row->params, $row->params);
			if (!$isExactMatch)
				break;
			$lastRow = $row;
		}
		if ($rowsCount != $slugsCount)
			$isExactMatch = false;
		if (false != $lastRow)
			$lastRow->isExactMatch = $isExactMatch;
		return $lastRow;
	}

	const URI_DELIMITER = '/';
}

